module uart_rx
#(
    parameter CLK_FREQ = 50000000,
    parameter UART_BPS = 115200
)
(
    input               clk         ,  //系统时钟
    input               rst_n       ,  //系统复位，低有效
    input               rxd    ,    //UART接收端口
    output  reg         dout_done,  //UART接收完成信号 ____↑￣|__
    output  reg  [7:0]  dout_data   //UART接收到的数据
);


localparam BAUD_CNT_MAX = CLK_FREQ/UART_BPS; //为得到指定波特率，对系统时钟计数BPS_CNT次

//reg define
reg          rxd_d0;
reg          rxd_d1;
reg          rxd_d2;
reg          rx_flag    ;  //接收过程持续为1
reg  [3:0 ]  cnt_rx     ;  //接收数据计数器
reg  [15:0]  cnt_baud   ;  //波特率计数器
reg  [7:0 ]  rx_data_temp  ;  //接收数据寄存器

//起始标志 ____↑￣|________
wire        start_en;

//针对异步信号的同步处理
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        rxd_d0 <= 1'b0;
        rxd_d1 <= 1'b0;
        rxd_d2 <= 1'b0;
    end
    else begin
        rxd_d0 <= rxd;
        rxd_d1 <= rxd_d0;
        rxd_d2 <= rxd_d1;
    end
end

//给接收标志赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n)
        rx_flag <= 1'b0;
    else if(start_en)    //检测到起始位
        rx_flag <= 1'b1; //接收过程中，标志信号rx_flag拉高
    //在停止位一半的时候，即接收过程结束，标志信号rx_flag拉低
    else if((cnt_rx == 4'd9) && (cnt_baud == BAUD_CNT_MAX/2 - 1'b1))
        rx_flag <= 1'b0;
    else
        rx_flag <= rx_flag;
end

/*
  捕获rxd下降沿(起始位)，start_en___↑￣|__
*/
assign start_en = rxd_d2 & (~rxd_d1) & (~rx_flag);

//波特率的计数器赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        cnt_baud <= 16'd0;
    else if(rx_flag) begin     //处于接收过程时，波特率计数器（cnt_baud）进行循环计数
        if(cnt_baud < BAUD_CNT_MAX - 1'b1)
            cnt_baud <= cnt_baud + 16'b1;
        else 
            cnt_baud <= 16'd0; //计数达到一个波特率周期后清零
    end    
    else
        cnt_baud <= 16'd0;     //接收过程结束时计数器清零
end

//对接收数据计数器（cnt_rx）进行赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        cnt_rx <= 4'd0;
    else if(rx_flag) begin                  //处于接收过程时cnt_rx才进行计数
        if(cnt_baud == BAUD_CNT_MAX - 1'b1) //当波特率计数器计数到一个波特率周期时
            cnt_rx <= cnt_rx + 1'b1;        //接收数据计数器加1
        else
            cnt_rx <= cnt_rx;
    end
    else
        cnt_rx <= 4'd0;                     //接收过程结束时计数器清零
end        

//根据cnt_rx来寄存rxd端口的数据
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) 
        rx_data_temp <= 8'b0;
    else if(rx_flag) begin                           //系统处于接收过程时
        if(cnt_baud == BAUD_CNT_MAX/2 - 1'b1) begin  //判断cnt_baud是否计数到数据位的中间
           case(cnt_rx)
               4'd1 : rx_data_temp[0] <= rxd_d2;   //寄存数据的最低位
               4'd2 : rx_data_temp[1] <= rxd_d2;
               4'd3 : rx_data_temp[2] <= rxd_d2;
               4'd4 : rx_data_temp[3] <= rxd_d2;
               4'd5 : rx_data_temp[4] <= rxd_d2;
               4'd6 : rx_data_temp[5] <= rxd_d2;
               4'd7 : rx_data_temp[6] <= rxd_d2;
               4'd8 : rx_data_temp[7] <= rxd_d2;   //寄存数据的高低位
               default : ;
            endcase  
        end
        else
            rx_data_temp <= rx_data_temp;
    end
    else
        rx_data_temp <= 8'b0;
end        

//给接收完成信号和接收到的数据赋值
always @(posedge clk or negedge rst_n) begin
    if(!rst_n) begin
        dout_done <= 1'b0;
        dout_data <= 8'b0;
    end
    //当接收数据计数器计数到停止位，且cnt_baud计数到停止位的中间时
    else if(cnt_rx == 4'd9 && cnt_baud == BAUD_CNT_MAX/2 - 1'b1) begin
        dout_done <= 1'b1     ;  //拉高接收完成信号
        dout_data <= rx_data_temp;  //并对UART接收到的数据进行赋值
    end    
    else begin
        dout_done <= 1'b0;
        dout_data <= dout_data;
    end
end

endmodule